package com.nsoft.html5npub.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

import com.nsoft.html5npub.utils.Constant;

import com.nsoft.html5npub.business.dbengine;
import com.nsoft.html5npub.objects.BookItem;
import com.nsoft.html5npub.objects.BookList;
import com.nsoft.html5npub.objects.Settings;

public class engine {
	private static String engineServer=Constant.EMPTY;
	private static String engineParams=Constant.EMPTY;
	private static ArrayList<BookItem> bookList=
						new ArrayList<BookItem>();
	
	public static String getEngineServer() {
		return engineServer;
	}
	public static void setEngineServer(String engineServer) {
		engine.engineServer = engineServer;
	}

	public static String getEngineParams() {
		return engineParams;
	}
	public static void setEngineParams(String engineParams) {
		engine.engineParams = engineParams;
	}
	
	public static ArrayList<BookItem> getBookList(){
		return engine.bookList;
	}
	
	public static BookItem getBookItem(int idx){
		return bookList.get(idx);
	}
	
	public static BookItem getBookItemById(String bookId){
		for (int i=0;i<bookList.size();i++){
			if (bookList.get(i).getContentId().
						equals(bookId)){
				return bookList.get(i);
			}
		}
		return null;
	}
	
	public static void addBookItem(BookItem book){
		bookList.add(book);
	}
	
	public static void insertBookItem(
					BookItem book,int index){
		bookList.add(index,book);
	}
	
	public static void removeBookItem(int idx){
		bookList.remove(idx);
	}
	
	public static void removeAllBooks(){
		bookList=new ArrayList<BookItem>();
		bookList.clear();
	}
	
	public static int getBookNumber(){
		return bookList.size();
	}
		
	public static String[] getBookTitles(){
		if (bookList==null || bookList.size()==0){
			return new String[0]; 
		}
		
		int numOfBooks=bookList.size();
		String[] titles=new String[numOfBooks];
		for (int i=0;i<numOfBooks;i++){
			titles[i]=bookList.get(i).getContentName();
		}
		return titles;
	}
	
	public static String[] getBookThumbImagePaths(){
		if (bookList==null || bookList.size()==0){
			return new String[0]; 
		}
		
		int numOfBooks=bookList.size();
		String[] thumbImgs=new String[numOfBooks];
		for (int i=0;i<numOfBooks;i++){
			thumbImgs[i]=bookList.get(i).getImgPath();
		}
		return thumbImgs;
	}
	
	/**
	 * this function is deprecated since we never intend
	 * to download directly from Internet.
	 * @return
	 */
	public static Bitmap[] getBookThumbBitmaps(){
		if (bookList==null || bookList.size()==0){
			return new Bitmap[0];
		}
		
		int thumbSize=bookList.size();
		Bitmap[] bmps=new Bitmap[thumbSize];
		for (int i=0;i<thumbSize;i++){
			bmps[i]=getBitmapFromURL(
					bookList.get(i).getImgPath());
		}
		return bmps;
	}
	
	public static Bitmap[] getBookThumbBitmapsFromHistory(){
		if (bookList==null || bookList.size()==0){
			return new Bitmap[0];
		}
		int thumbSize=bookList.size();
		Bitmap[] bmps=new Bitmap[thumbSize];
		for (int i=0;i<thumbSize;i++){
			bmps[i]=getBitmapFromFile(
					bookList.get(i).getImgPath());
		}
		return bmps;
	}
	
	public static Bitmap[] getBookThumbFromHistory(
					int startIndex,int endIndex){
		if (bookList==null || bookList.size()==0){
			return new Bitmap[0];
		}
		int thumbSize=bookList.size();
		if (startIndex>endIndex){
			return new Bitmap[0];
		}
		if (startIndex<0)
			startIndex=0;
		else if (startIndex>=thumbSize){
			return new Bitmap[0];
		}
		
		if (endIndex>=thumbSize){
			endIndex=thumbSize-1;
		}else if (endIndex<0){
			return new Bitmap[0];
		}
		
		Bitmap[] bmps=new Bitmap[endIndex-startIndex+1];
		for (int i=startIndex;i<=endIndex;i++){
			bmps[i]=getBitmapFromFile(
					bookList.get(i).getImgPath());
		}
		return bmps;
	}
	
	public static Bitmap getThumbFromBook(int bookIndex){
		if (bookList.size()==0)
			return null;
		
		BookItem book=bookList.get(bookIndex);
		if (book!=null)
			return getBitmapFromFile(
						book.getImgPath());
		return null;
	}
	
	public static Bitmap getThumbFromBook(BookItem book){
		if (book!=null)
			return getBitmapFromFile(
						book.getImgPath());
		return null;
	}
	
	/**
	 * get list of icons from server.<br>
	 * icons are located in a path that can be recognized and
	 * downloaded from server. for displaying, we need to 
	 * download icons on a separate thread.
	 * 
	 * @return
	 * @throws IOException
	 */
	public static boolean getIconsFromServer() 
							throws IOException{
		
		String iconFolder=utils.getIconPath();
		utils.delete(new File(iconFolder));
		
		String icon_url=getEngineServer()+
						Settings.getIconUrl();
		
		InputStream input=null;
		FileOutputStream output=null;
		
		try{
			URL url=new URL(icon_url);
			String fileName=icon_url.substring(
					icon_url.lastIndexOf(
							Constant.PATH_SPLITTER)+1);
			String outputFilePath=utils.getBookPath()+
						Constant.PATH_SPLITTER+fileName;
			
			HttpURLConnection connection= 
	        		(HttpURLConnection)url.openConnection();
	        connection.setDoInput(true);
	        connection.setConnectTimeout(Constant.
	        		dataserver.SERVER_CONNECT_TIMEOUT);
	        connection.connect();
	        
	        int retCode=connection.getResponseCode();
	        if (retCode==Constant.dataserver.SERVER_CODE_OK){
	        	input=connection.getInputStream();
	        }else{
	        	throw new IOException(Constant.
		        		dataserver.ERROR_RESPONSE_INCORRECT.
		        		replace(Constant.CHR_REPLACE,
		        				Integer.toString(retCode)));
	        }
	        
	        if (input==null){
	        	throw new IOException(Constant.dataserver.
	        			ERROR_RESPONSE_STREAM_UNAVAILABLE);
	        }
	        
	        output=new FileOutputStream(
	        			outputFilePath,false);
	        
	        byte[] buff=new byte[1024];
	        int bytesRead=0;
	        while((bytesRead=input.read(buff))!=-1){
	        	output.write(buff,0,bytesRead);
	        }
	        output.flush();
	        output.close();
	        
	        utils.unzipFile(null,outputFilePath,
	        				utils.getBookPath(),true);
	        utils.delete(new File(outputFilePath));
	        
			return true;
		}catch(IOException e){
			Log.v("engine.getIconsFromServer()",
		    	e.getClass().toString()+": "+e.getMessage());
			throw e;
		}finally{
			try {
				if (input!=null){
					input.close();
				}
			}catch(IOException e){ }
		}
	}
	
	public static boolean getBookListFromHistory()
							throws Exception{
		return getBookListFromXml();
	}
	
	public static void getBookListFromServer() 
							throws Exception{
		getBookListFromServer(
			getEngineServer(),getEngineParams());
	}
	
	private static void getBookListFromServer(
			String server,String params) throws Exception{
		/**
		 * clear the book list
		 */
		removeAllBooks();
		
		String thumbPath=utils.getThumbPath();
		String url=server+
				Constant.UTIL_SERVER_APPLICATION+
				Constant.PATH_SPLITTER+params;
		
		/**
		 * check the Internet connection
		 * check in 15 seconds
		 */
		InputStream is=null;
		try{
			HttpURLConnection httpConn=
					(HttpURLConnection)
						new URL(url).openConnection();
			httpConn.setConnectTimeout(
				Constant.dataserver.SERVER_CONNECT_TIMEOUT);
			httpConn.setDoInput(true);
			httpConn.connect();
		
			int retCode=httpConn.getResponseCode();
			if (httpConn.getResponseCode()==
					Constant.dataserver.SERVER_CODE_OK){
				is=httpConn.getInputStream();
			}else{
				throw new Exception(
					Constant.dataserver.
					ERROR_RESPONSE_INCORRECT.replace(
							Constant.CHR_REPLACE,
							Integer.toString(retCode)));
			}
		}catch(IOException e){
			throw e;
		}
		
		if (is==null)
			throw new Exception(Constant.dataserver.
					ERROR_RESPONSE_STREAM_UNAVAILABLE);
		
		/**
		 * add the list from database at first
		 */
		for (BookItem book:dbengine.dbBookList.values()){
			addBookItem(book);
		}
		
		/**
		 * download the book list after checking Internet
		 */
		try{
			DocumentBuilderFactory docBuilderFactory = 
						DocumentBuilderFactory.newInstance();
	        DocumentBuilder docBuilder = 
	        		docBuilderFactory.newDocumentBuilder();
			Document doc=docBuilder.parse(is);
	        
	        NodeList list=doc.getElementsByTagName(
	        			Constant.xml.XML_TAG_CONTENT);
	        NodeList c_nodes=null;
	        BookItem book=null;
	        String nodeCont,nodeName,savedImgPath;
	        boolean isBookGotBugs=false;
			for (int i=0;i<list.getLength();i++){
				c_nodes=list.item(i).getChildNodes();
				book=new BookItem();
				isBookGotBugs=false;
				
				for (int j=0;j<c_nodes.getLength();j++){
					nodeCont=c_nodes.item(j).getTextContent();
					nodeName=c_nodes.item(j).getNodeName();
					if (nodeName.equalsIgnoreCase(
							Constant.xml.XML_TAG_CONTENTNAME)){
						if (nodeCont.equals(Constant.EMPTY)){
							isBookGotBugs=true;
							break;
						}
						book.setContentName(nodeCont);
					}else if (nodeName.equalsIgnoreCase(
							Constant.xml.XML_TAG_CONTENTID)){
						book.setContentId(nodeCont);
					}else if (nodeName.equalsIgnoreCase(
							Constant.xml.XML_TAG_DOWNLOADPATH)){
						book.setDownloadPath(nodeCont);
					}else if (nodeName.equalsIgnoreCase(
							Constant.xml.XML_TAG_FILENAME)){
						book.setFilename(nodeCont);
					}else if (nodeName.equalsIgnoreCase(
							Constant.xml.XML_TAG_IMGPATH)){
						savedImgPath=saveThumbToStorage(
									nodeCont,thumbPath);
						if (savedImgPath.equals(Constant.EMPTY)){
							isBookGotBugs=true;
							break;
						}
						book.setImgPath(savedImgPath);
					}else if (nodeName.equalsIgnoreCase(
							Constant.xml.XML_TAG_VERSION)){
						book.setVersion(nodeCont);
					}else if (nodeName.equalsIgnoreCase(
							Constant.xml.XML_TAG_STATE)){
						book.setState(nodeCont);
					}else if (nodeName.equalsIgnoreCase(
							Constant.xml.XML_TAG_REDOWNLOAD)){
						book.setReDownload(nodeCont);
					}
				}
				if (!isBookGotBugs&&
						!dbengine.checkBookDownloaded(
								book.getContentId())){
					addBookItem(book);
				}else{
					isBookGotBugs=false;
				}
			}
			
			saveBookListToXml();
		
		}catch(Exception e){
			throw e;
		}finally{
			if (is!=null)
				is.close();
		}
		
	}

	/**
	 * the order of getting book list: <br>
	 * 1. get the book list from database <br>
	 * 2. get the list from XML file
	 * 3. combine these two lists: keep the list from 
	 * 	  step 1 and omit the items from list#2 which 
	 *	  exist in list #1 
	 * 
	 * @return
	 * @throws Exception
	 */
	private static boolean getBookListFromXml()
						throws Exception{
		/**
		 * 0. clear book list
		 */
		removeAllBooks();
		
		/**
		 * 1. get book list from database first
		 */
		for (BookItem book:dbengine.dbBookList.values()){
			addBookItem(book);
		}
		/**
		 * 2. get book list from XML file, and merge with 
		 * list from database
		 */
		String xmlHistoryFile=utils.getHistoryListFile();
		try{
			BookList bookListContainer=
				(BookList)utils.decodeXml(
						new File(xmlHistoryFile),
						BookList.class);
			
			for (BookItem book:bookListContainer.
								getBookItems()){
				if (!dbengine.checkBookDownloaded(
							book.getContentId())){
					addBookItem(book);
				}
			}
			/*
			bookList=bookListContainer.getBookItems();
			*/
			return true;
		}catch(Exception e){
			Log.v("engine.saveBookListToXml()",
				e.getClass().toString()+": "+e.getMessage());
			throw e;
		}
	}
	
	private static String saveBookListToXml() 
						throws Exception {
		String xmlFilePath=utils.getHistoryListFile();
		try{
			BookList bookListContainer=new BookList();
			bookListContainer.setBookItems(bookList);
			utils.encodeXml(bookListContainer,
							new File(xmlFilePath));
			return xmlFilePath;
		}catch(Exception e){
			Log.v("engine.saveBookListToXml()",
				e.getClass().toString()+": "+e.getMessage());
			throw e;
		}
	}
	
	private static String saveThumbToStorage(
						String src,String outPath){
		InputStream input=null;
		OutputStream output=null;
		try{
			URL url=new URL(src);
			int lastOfExt=src.lastIndexOf(Constant.PATH_DOT);
			String fileName=src.substring(
					src.lastIndexOf(Constant.PATH_SPLITTER)+1,
					lastOfExt)+Constant.PATH_DOT+
					Constant.bmp.EXTENSION;
			String outputFilePath=outPath+
					Constant.PATH_SPLITTER+fileName;
			
			HttpURLConnection connection=
	        		(HttpURLConnection)url.openConnection();
	        connection.setDoInput(true);
	        connection.setConnectTimeout(
	        		Constant.dataserver.SERVER_CONNECT_TIMEOUT);
	        connection.connect();
	        
	        int retCode=connection.getResponseCode();
	        if (retCode==Constant.dataserver.SERVER_CODE_OK){
	        	input=connection.getInputStream();
	        }else{
	        	return Constant.EMPTY;
	        }
	        
	        if (input==null){
	        	return Constant.EMPTY;
	        }
	        
	        int cntLength=connection.getContentLength();
	        
	        Bitmap bmp=utils.createBitmap(input,cntLength);
	        output=new FileOutputStream(outputFilePath,false);
	        bmp.compress(Bitmap.CompressFormat.PNG,
	        		Constant.bmp.QUALITY,output);
	        /*
	        output=new FileOutputStream(outputFilePath,false);
	        byte[] buff=new byte[1024];
	        int bytesRead=0;
	        while((bytesRead=input.read(buff))!=-1){
	        	output.write(buff,0,bytesRead);
	        }
	        output.flush();
			*/
	        
	        return outputFilePath;
		}catch(IOException e){
			Log.v("engine.saveThumbToStorage()",
		    		"["+e.getClass().toString()+"] "+e.getMessage());
		    	return Constant.EMPTY;
		}finally{
			try {
				if (input!=null){
					input.close();
				}
				if (output!=null){
					output.close();
				}
			}catch(IOException e){ }
		}
	}
	
	/**
	 * delete a downloaded book from list view
	 * 
	 * @param c
	 * @param adapter
	 * @param book
	 * @throws Exception
	 */
	public static void deleteDownloadedBook(Context c,
		BookListAdapter adapter,BookItem book,int pos) 
								throws Exception {
		
	}
	
	/**
	 * open bitmap directly from local storage
	 * 
	 * @param src
	 * @return
	 */
	public static Bitmap getBitmapFromFile(String src){
		return BitmapFactory.decodeFile(src);
	}
	
	/**
	 * this function support getting bitmap from Internet.
	 * this is rarely used since we're not intending download
	 * anything on-the-fly.
	 * 
	 * @param src
	 * @return
	 */
	private static Bitmap getBitmapFromURL(String src){
		try {
	        URL url=new URL(src);
	        HttpURLConnection connection=
	        		(HttpURLConnection)url.openConnection();
	        connection.setDoInput(true);
	        connection.connect();
	        InputStream input=connection.getInputStream();
	        Bitmap myBitmap=BitmapFactory.decodeStream(input);
	        return myBitmap;
	    }catch(IOException e){
	    	Log.v("engine.getBitmapFromURL()",
	    		"["+e.getClass().toString()+"] "+e.getMessage());
	    	return null;
	    }
	}

}
